import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:bluetooth_protocols_parser_plugin/library.dart';
import 'package:bluetooth_protocols_plugin/library.dart';
import 'package:flutter/material.dart';

import '../enum/open_file_type.dart';
import '../tools/file_utils.dart';

typedef ProgressCallback = void Function(double progress);
typedef TotalSizeCallback = void Function(int totalSize);
typedef OtaStatusCallback = void Function(OtaUpgradeStatus status);

// OTA升级管理器
class OtaUpgradeManager {
  // 协议
  final Protocol protocol;

  // 目标设备类型
  final TargetDeviceType targetDeviceType;

  // 本地固件文件路径
  final String localFirmwareUrl;

  // 新版本号
  final String newVersion;

  // 文件总大小 - 回调函数
  final TotalSizeCallback? onTotalSize;

  // 文件传输进度 - 回调函数
  final ProgressCallback onProgress;

  // ota升级状态 - 回调函数
  final OtaStatusCallback onStatus;

  // 待用户操作 - 回调函数
  final WaitingUserActionCallback onUserActionRequired;

  // 每次传输的数据包大小
  final int packetSize;

  // OTA升级的固定文件路径
  final String _flashFilePath = 'update.bin';

  // 文件句柄
  int? _fileHandle;

  // 最大重试次数（包括首次尝试）
  final int _maxRetryCount = 2;

  // 当前重试次数
  int _currentRetryCount = 0;

  // 帧头的长度0xAA 0xAA Format(1) Key(0~1) Tab（0~4）
  int _headerMaxLength = 8;

  // 帧尾的长度 check（0~4）
  int _tailMaxLength = 4;

  // 冗余长度
  int _redundantLength = 10;

  // 构造函数
  OtaUpgradeManager(
    this.protocol,
    this.targetDeviceType,
    this.localFirmwareUrl,
    this.newVersion, {
    required this.onProgress,
    required this.onStatus,
    required this.onUserActionRequired,
    required this.packetSize,
    this.onTotalSize,
  });

  // MARK: - 开始OTA升级
  Future<void> start() {
    debugPrint('Lorin startOTA: $targetDeviceType, $localFirmwareUrl');
    // 重置重试计数器
    _currentRetryCount = 0;
    return _startWithRetry();
  }

  // 带重试的启动方法
  Future<void> _startWithRetry({int? existingFileSize}) async {
    _currentRetryCount++;
    debugPrint('开始OTA升级尝试 $_currentRetryCount/$_maxRetryCount');

    try {
      await _startOTAProcess(existingFileSize: existingFileSize);
    } catch (e) {
      // 如果是校验文件失败且还有重试机会
      if (e is OtaException &&
          e.status == OtaUpgradeStatus.verifyFileFailed &&
          _currentRetryCount < _maxRetryCount) {
        debugPrint('⚠️ 文件校验失败，准备重试 (${_currentRetryCount}/$_maxRetryCount)');

        // 关闭文件句柄（如果存在）
        if (_fileHandle != null) {
          try {
            await _closeFileWithPrint(targetDeviceType);
          } catch (closeError) {
            debugPrint('⚠️ 关闭文件时发生错误: $closeError');
          }
          _fileHandle = null;
        }

        // 延迟一段时间后重试
        await Future.delayed(Duration(seconds: 1));

        // 递归调用重试
        return _startWithRetry(existingFileSize: 0); // 重试时从头开始传输
      } else {
        // 其他错误或已达到最大重试次数，直接抛出
        rethrow;
      }
    }
  }

  // 提取主要的OTA流程到单独方法
  Future<void> _startOTAProcess({int? existingFileSize}) {
    return protocol.queryFirmwareUpdateStatus(targetDeviceType).then((status) {
      if (status == FirmwareUpgradeStatus.idle) {
        // 处于空闲状态，可以进行OTA升级
        onStatus.call(OtaUpgradeStatus.idle);
        return Future.value();
      }
      // 当前不处于空闲状态，无法进行OTA升级
      throw OtaException(OtaUpgradeStatus.busy);
    }).then((_) {
      debugPrint("✅ 允许固件升级，准备打开文件");
      onStatus.call(OtaUpgradeStatus.openFile);
      // 打开文件
      return protocol.openFile(
          targetDeviceType, OpenFileType.readWriteCreate, _flashFilePath);
    }).then((openRsp) {
      _fileHandle = openRsp.fileHandle;
      if (_fileHandle == null) {
        debugPrint("❌ 文件打开失败");
        throw OtaException(OtaUpgradeStatus.openFileFailed);
      }
      // 文件打开成功，开始传输固件
      onStatus.call(OtaUpgradeStatus.openFileSuccess);
      debugPrint("✅ 文件打开成功");
      onStatus.call(OtaUpgradeStatus.verifyFile);
      debugPrint(
          "✅ 已存在的文件大小: ${openRsp.existingFileSize}, 校验值: ${openRsp.existingFileCrc32}");
      // 读取本地固件文件，计算校验值
      return FileUtils.calculateFileCrc32(
              localFirmwareUrl, 0, openRsp.existingFileSize)
          .then((fileCrc32) {
        if (fileCrc32 == openRsp.existingFileCrc32) {
          onStatus.call(OtaUpgradeStatus.verifyFileSuccess);
          debugPrint("✅ 已存在文件校验通过，可以断点续传");
          return Future(() => existingFileSize ?? openRsp.existingFileSize);
        } else {
          debugPrint("⚠️ 已存在文件校验失败，新校验值: $fileCrc32, 从0开始传输");
          return Future(() => 0); // 从头开始传输
        }
      });
    }).then((offset) {
      onStatus.call(OtaUpgradeStatus.writeFile);
      // 开始写入文件
      debugPrint("✅ 准备从偏移量 $offset 开始写入文件");
      int _packetOffset = offset;
      return _transferFileByPackets(
          filePath: localFirmwareUrl,
          packetSize:
              packetSize - _headerMaxLength - _tailMaxLength - _redundantLength,
          startOffset: _packetOffset,
          onPacketSuccess:
              (fileLength, packetOffset, packetBytes, isLastPacket) {
            onStatus.call(OtaUpgradeStatus.transferring);
            debugPrint(
                "✅ 传输偏移位置: $packetOffset, 传输: ${packetBytes.length} 字节, 是否最后一包: $isLastPacket");
            return protocol
                .writeFile(
                    targetDeviceType, _fileHandle!, packetOffset, packetBytes)
                .then((writeRsp) {
              // 返回值表示实际写入的字节数，当与packetBytes相等时表示写入成功
              if (isLastPacket) {
                debugPrint("✅ 固件传输完成");
                return Future.value(true);
              } else {
                if (writeRsp.writeLength != packetBytes.length) {
                  debugPrint("⚠️ 实际写入大小与期望不符");
                  return Future.value(false);
                }
                _packetOffset += writeRsp.writeLength;
                // 更新进度
                onProgress(fileLength > 0 ? _packetOffset / fileLength : 0.0);
                return Future.value(true);
              }
            });
          });
    }).then((_) {
      debugPrint("✅ 文件写入完成，执行校验文件");
      onStatus.call(OtaUpgradeStatus.verifyFile);
      return protocol
          .verifyReadFile(targetDeviceType, _fileHandle!, 0)
          .then((verifyReadFileRsp) {
        debugPrint("✅ 车辆文件的校验值: ${verifyReadFileRsp.offsetCrc32}");
        return FileUtils.calculateFullFileCrc32(localFirmwareUrl);
      }).then((fullFile) {
        debugPrint("✅ 本地文件总大小: ${fullFile.$1}, 校验值: ${fullFile.$2}");
        // 从偏移量0开始校验
        return protocol.verifyWriteFile(
            targetDeviceType, _fileHandle!, fullFile.$1, 0, fullFile.$2);
      });
    }).then((verifyWriteFileRsp) {
      if (verifyWriteFileRsp.isSuccess && verifyWriteFileRsp.fileCrc32Success) {
        onStatus.call(OtaUpgradeStatus.verifyFileSuccess);
        debugPrint("✅ 校验完成，准备关闭文件");
        return _closeFileWithPrint(targetDeviceType);
      }
      throw OtaException(OtaUpgradeStatus.verifyFileFailed);
    }).then((closeFileSuccess) {
      if (closeFileSuccess) {
        onStatus.call(OtaUpgradeStatus.closeFileSuccess);
        debugPrint("✅ 文件关闭完成，设备会自动启动固件升级");
        return Future.value();
      }
      throw OtaException(OtaUpgradeStatus.closeFileFailed);
    }).then((startFirmwareUpdateSuccess) {
      onStatus.call(OtaUpgradeStatus.waitingUserAction);
      debugPrint("✅ 等待用户操作确认");
      return _handerUserActionRequired(onUserActionRequired);
    }).then((_) {
      debugPrint("✅ 用户操作完成，等待设备重启完成，查询固件版本号");
      return _pollFirmwareVersion(targetDeviceType).then((isVersionMatched) {
        if (isVersionMatched) {
          onStatus.call(OtaUpgradeStatus.upgradeSuccess);
          debugPrint("✅ 固件版本号匹配，升级成功");
        } else {
          onStatus.call(OtaUpgradeStatus.upgradeFailed);
          debugPrint("❌ 固件版本号不匹配，升级失败");
        }
        return Future.value();
      });
    }).catchError((err) {
      debugPrint("❌ OTA升级过程中发生错误: $err");
      // 在catchError中关闭文件，但不处理重试逻辑
      _closeFileWithPrint(targetDeviceType);
      throw err;
    });
  }

  // 处理等待用户操作
  Future<void> _handerUserActionRequired(
      WaitingUserActionCallback onUserActionRequired) async {
    Completer<void> completer = Completer<void>();
    onUserActionRequired(UserActionType.none, (bool userConfirmed) {
      if (!completer.isCompleted) {
        debugPrint("✅ 用户操作回调完成，用户选择: ${userConfirmed ? "确认重启设备" : "未确认重启设备"}");
        completer.complete();
      }
    });
    return completer.future;
  }

  // 循环查询固件版本号，等待【time】才开始比对
  Future<bool> _pollFirmwareVersion(TargetDeviceType targetDeviceType,
      {int delayedInterval = 0}) async {
    const int maxRetries = 30; // 最大重试次数
    const Duration retryInterval = Duration(seconds: 2); // 重试间隔时间
    // 延时等待设备稳定
    await Future.delayed(Duration(milliseconds: delayedInterval));

    for (int attempt = 0; attempt < maxRetries; attempt++) {
      try {
        debugPrint("✅ 查询固件版本号，尝试次数: ${attempt + 1}");
        List<Map<String, dynamic>> version =
            await protocol.getFirmwareVersion();
        if (version.isNotEmpty) {
          // 固件信息存在device，appVersion等字段，取device==targetDevice的version字段
          String appVersion = version.firstWhere(
                  (element) =>
                      element['device_id'] == targetDeviceType.rawValue,
                  orElse: () => {})['app_version'] ??
              '';
          if (appVersion == newVersion) {
            return true;
          } else {
            // 版本号不匹配，继续重试
            debugPrint("⚠️ 版本号不匹配，期望: $newVersion, 实际: $appVersion");
          }
        }
      } catch (e) {
        debugPrint("⚠️ 查询固件版本号时发生错误: $e");
      }
      // 等待一段时间后重试
      await Future.delayed(retryInterval);
    }
    return false;
  }

  // 关闭文件
  Future<bool> _closeFileWithPrint(TargetDeviceType targetDeviceType) async {
    if (_fileHandle == null) {
      debugPrint("⚠️ 关闭文件句柄为空");
      throw OtaException(OtaUpgradeStatus.closeFileFailed);
    }
    try {
      debugPrint("✅ 关闭文件");
      await protocol.closeFile(targetDeviceType, _fileHandle!);
      return true;
    } catch (e) {
      debugPrint("⚠️ 关闭文件时发生错误: $e");
      throw OtaException(OtaUpgradeStatus.closeFileFailed);
    }
  }

  // 按包传输文件（除去会转义字符的长度）
  Future<void> _transferFileByPackets({
    required String filePath,
    required int packetSize,
    required Future<bool> Function(
      int totalSize,
      int offset,
      Uint8List data,
      bool isLastPacket,
    ) onPacketSuccess,
    int startOffset = 0,
    List<int> escapeCharacters = LorinConstant.escapeBytes,
  }) async {
    final file = File(filePath);
    final fileLength = await file.length();
    int currentOffset = startOffset;

    final randomAccessFile = await file.open(mode: FileMode.read);

    try {
      while (currentOffset < fileLength) {
        final remainingBytes = fileLength - currentOffset;

        // 计算当前数据包大小
        int currentPacketSize =
            remainingBytes < packetSize ? remainingBytes : packetSize;

        await randomAccessFile.setPosition(currentOffset);
        Uint8List originalData = await randomAccessFile.read(currentPacketSize);

        // 计算转义字符数量
        int escapeCount = 0;
        if (escapeCharacters.isNotEmpty) {
          escapeCount = _countEscapeCharacters(originalData, escapeCharacters);
        }

        // 实际有效数据长度 = 读取的数据长度 - 转义字符数量
        int actualDataSize = currentPacketSize - escapeCount;

        // 根据actualDataSize截取有效数据
        Uint8List dataToSend =
            Uint8List.fromList(originalData.sublist(0, actualDataSize));

        // 判断是否为最后一个数据包（基于原始文件位置）
        final isLastPacket = currentOffset + currentPacketSize >= fileLength;

        // 等待成功回调完成后再继续下一个包
        final isSuccess = await onPacketSuccess(
            fileLength, currentOffset, dataToSend, isLastPacket);

        if (isSuccess) {
          if (isLastPacket) {
            debugPrint("✅ 最后一个数据包传输成功，传输完成");
            break;
          }
        } else {
          debugPrint("❌ 数据包传输失败，停止传输");
          break;
        }

        // 关键：使用原始读取的字节数作为偏移量增量
        currentOffset += actualDataSize;
      }
    } finally {
      await randomAccessFile.close();
    }
  }

  // 计算数据中包含的转义字符数量
  int _countEscapeCharacters(Uint8List data, List<int> escapeCharacters) {
    int count = 0;
    for (int byte in data) {
      if (escapeCharacters.contains(byte)) {
        count++;
      }
    }
    return count;
  }
}
